package com.lgj.auth.config;

import com.lgj.auth.code.customize.mobile.SmsVerificationCodeTokenGranter;
import com.lgj.auth.code.customize.weChat.WeChatTokenGranter;
import com.lgj.auth.code.handler.exception.AuthTokenExceptionHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.CompositeTokenGranter;
import org.springframework.security.oauth2.provider.TokenGranter;
import org.springframework.security.oauth2.provider.approval.ApprovalStore;
import org.springframework.security.oauth2.provider.approval.TokenApprovalStore;
import org.springframework.security.oauth2.provider.approval.TokenStoreUserApprovalHandler;
import org.springframework.security.oauth2.provider.approval.UserApprovalHandler;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;

import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;


/**
 * 认证服务配置
 * @author: guangjie.liao
 * @Date: 2022/11/16 19:19
 */
@Slf4j
@Configuration
@EnableAuthorizationServer
public class OauthAuthorizationConfig extends AuthorizationServerConfigurerAdapter {
    @Autowired
    private DataSource dataSource;
    /**
     * 授权认证
     */
    private final AuthenticationManager authenticationManager;
    /**
     * 查询客户信息
     */
    private final UserDetailsService userDetailsService;
    /**
     * 账号密码生成，校验
     */
    private final PasswordEncoder passwordEncoder;
    /**
     * token存储
     */
    private final TokenStore tokenStore;
    /**
     * code码生成
     */
    private final AuthorizationCodeServices authorizationCodeServices;
    /**
     * 认证异常处理
     */
    private final AuthTokenExceptionHandler authTokenExceptionHandler;
    /**
     * token生成
     */
    private final AuthorizationServerTokenServices authorizationServerTokenServices;

    public OauthAuthorizationConfig(AuthenticationManager authenticationManager,
                                    UserDetailsService userDetailsService,
                                    PasswordEncoder passwordEncoder,
                                    TokenStore tokenStore,
                                    AuthorizationCodeServices authorizationCodeServices,
                                    AuthTokenExceptionHandler authTokenExceptionHandler
            , AuthorizationServerTokenServices authorizationServerTokenServices) {
        this.authenticationManager = authenticationManager;
        this.userDetailsService = userDetailsService;
        this.passwordEncoder = passwordEncoder;
        this.tokenStore = tokenStore;
        this.authorizationCodeServices = authorizationCodeServices;
        this.authTokenExceptionHandler = authTokenExceptionHandler;
        this.authorizationServerTokenServices = authorizationServerTokenServices;
    }

    /**
     * 客户端查询配置
     * @return
     */
    @Bean
    public ClientDetailsService jdbcClientDetailsService(){
        return new JdbcClientDetailsService(dataSource);
    }

    /**
     * 客户端详情配置
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.withClientDetails(jdbcClientDetailsService());
    }


    /**
     * UserApprovalHandler 有三个实现
     * 1.ApprovalStoreUserApprovalHandler 通过比对oauth_client_details 表配置autoapprove字段来记住授权
     * 2.TokenStoreUserApprovalHandler 通过access_token 为key来缓存用户授权内容，默认缓存一个月
     * 3.DefaultUserApprovalHandler 每次都让用户授权，不记住
     * @return
     */
    @Bean
    public TokenStoreUserApprovalHandler userApprovalHandler(){
        TokenStoreUserApprovalHandler handler = new TokenStoreUserApprovalHandler();
        handler.setTokenStore(tokenStore);
        handler.setRequestFactory(new DefaultOAuth2RequestFactory(jdbcClientDetailsService()));
        handler.setClientDetailsService(jdbcClientDetailsService());
        return handler;
    }



    /**
     * 配置端点（"/auth/**"）的安全访问
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security
                //设置密码编辑器
                .passwordEncoder(passwordEncoder)
                .allowFormAuthenticationForClients()
                //开启 /oauth/token_key 的访问权限控制
                .tokenKeyAccess("permitAll()")
                //开启 /oauth/check_token 验证端口认证权限访问
                .checkTokenAccess("permitAll()")
        ;
    }

    /**
     * 端点配置
     * 装载Endpoints所有相关的类配置（AuthorizationServer、TokenServices、TokenStore、ClientDetailsService、UserDetailsService）
     * 密码模式下配置认证管理器 AuthenticationManager
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        List<TokenGranter> granters = new ArrayList<>(Arrays.asList(endpoints.getTokenGranter()));
        // 添加手机短信验证码授权模式的授权者
        granters.add(
                new SmsVerificationCodeTokenGranter(
                        endpoints.getTokenServices(),
                        endpoints.getClientDetailsService(),
                        endpoints.getOAuth2RequestFactory(),
                        authenticationManager
                )
        );
        //添加微信授权登陆
        granters.add(
                new WeChatTokenGranter( endpoints.getTokenServices(),
                    endpoints.getClientDetailsService(),
                    endpoints.getOAuth2RequestFactory(),
                    authenticationManager
                )
        );


        CompositeTokenGranter tokenGranter = new CompositeTokenGranter(granters);
        // 配置授权服务器端点的属性 //认证管理器
        endpoints
                .tokenStore(tokenStore)
                .tokenGranter(tokenGranter)
                .userDetailsService(userDetailsService)
                .userApprovalHandler(userApprovalHandler())
                .authenticationManager(authenticationManager)
                .exceptionTranslator(authTokenExceptionHandler)
                .userApprovalHandler(userApprovalHandler())
                .authorizationCodeServices(authorizationCodeServices);

        endpoints.pathMapping("/oauth/confirm_access","/custom/confirm_access");
    }
}
